/*
 * 易族智汇（北京）科技有限公司 版权所有。
 * 未经许可，您不得使用此文件。
 * 官方地址：www.javamall.com.cn
 */
package com.enation.app.javashop.handler;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.enation.app.javashop.framework.JavashopConfig;
import com.enation.app.javashop.framework.security.model.TokenConstant;
import com.enation.app.javashop.framework.util.DbSecretUtil;
import com.enation.app.javashop.framework.util.StringUtil;
import com.enation.app.javashop.handler.annotation.Secret;
import com.enation.app.javashop.handler.annotation.SecretField;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.util.List;

/**
 * @author fk
 * @version v7.0
 * @since v7.2.3
 * 2021年11月19日10:29:05
 */
//@Aspect
//@Component
//@Order(100)
@Service
public class DbAspect {

    public static void main(String[] args) {
        
    }
    public static ThreadLocal<Boolean> skipThreadLocal = new ThreadLocal<>();

    @Autowired
    private JavashopConfig javashopConfig;

    /**
     * 是否需要跳过加解密操作
     * @return
     */
    private boolean isSkip() {

        if(!javashopConfig.isEncryptSetting()){
            return false;
        }

        if (skipThreadLocal.get()!=null) {
            return skipThreadLocal.get();
        }else {
            return false;
        }
    }
    @Around("insert()")
    public void aroundInsert( Object obj) throws Throwable {

        /**
         * 需要跳过加解密操作
         */
        if (isSkip()) {
            return;
        }

        Class<?> clz = obj.getClass();
        Secret secret = clz.getAnnotation(Secret.class);

        //实体类有加密注解，则需要加密
        if (secret != null) {

            //循环每个字段，查找加密字段
            ReflectionUtils.doWithFields(clz, field -> {

                SecretField secretField = field.getAnnotation(SecretField.class);
                if (secretField != null) {
                    // 这个字段需要重新赋值
                    ReflectionUtils.makeAccessible(field);
                    //进入这个注解，说明需要会话秘钥解密，且服务器秘钥加密
                    // 原来的值
                    Object originValue = field.get(obj);
                    //重新附上，使用服务器端秘钥加密后的密文
                    field.set(obj, DbSecretUtil.encrypt((String) originValue, TokenConstant.SECRET));
                }

            });
        }


    }

    /**
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("selectPage()")
    public Object aroundSelectPage(ProceedingJoinPoint point) throws Throwable {


        //执行查询
        Page result = (Page) point.proceed();

        /**
         * 需要跳过加解密操作
         */
        if (isSkip()) {
            return result;
        }

        //得到数据库查询的结果
        List records = result.getRecords();



        //加解密处理一下
        this.handleResult(records);

        return result;
    }


    @Around("selectList()")
    public Object aroundSelectList(ProceedingJoinPoint point) throws Throwable {
        //执行查询
        List result = (List) point.proceed();

        /**
         * 需要跳过加解密操作
         */
        if (isSkip()) {
            return result;
        }


        this.handleResult(result);
        return result;
    }

    @Around("selectById()")
    public Object aroundSelectById( Object entity ) throws Throwable {

        return this.aroundSelectOne(entity);
    }

    @Around("selectOne()")
    public Object aroundSelectOne(Object result ) throws Throwable {

        /**
         * 需要跳过加解密操作
         */
        if (isSkip()) {
            return result;
        }

        if (result == null) {
            return null;
        }
        Class<?> clz = result.getClass();
        //判断实体对象上是否有加解密注解，有的话，则服务器端秘钥解密，会话秘钥加密
        Secret secret = clz.getAnnotation(Secret.class);
        if (secret != null) {
            this.handleEntity(result);
        }
        return result;
    }


    @Around("updateById()")
    public void aroundUpdateById(ProceedingJoinPoint point) throws Throwable {

        /**
         * 需要跳过加解密操作
         */
        if (isSkip()) {
            return ;
        }

        Object[] args = point.getArgs();
        Object obj = args[0];

        if(obj == null){
            return ;
        }

        Class<?> clz = obj.getClass();
        Secret secret = clz.getAnnotation(Secret.class);

        //实体类有加密注解，则需要先解密
        if (secret != null) {

            //循环每个字段，查找加密字段
            ReflectionUtils.doWithFields(clz, field -> {

                SecretField secretField = field.getAnnotation(SecretField.class);
                if (secretField != null) {
                    // 这个字段需要重新赋值
                    ReflectionUtils.makeAccessible(field);
                    // 原来的值
                    Object originValue = field.get(obj);
                    //重新附上，使用服务器端秘钥加密后的密文
                    field.set(obj, DbSecretUtil.encrypt((String) originValue, TokenConstant.SECRET));
                }

            });
        }

    }

    @Around("update()")
    public void aroundUpdate(ProceedingJoinPoint point) throws Throwable {

         aroundUpdateById(point);
    }

    /**
     * 处理返回list
     *
     * @param list
     */
    private void handleResult(List list) {

        if (StringUtil.isNotEmpty(list)) {
            //得到Page中的泛型对象
            Class<?> clz = list.get(0).getClass();
            //判断实体对象上是否有加解密注解，有的话，则服务器端秘钥解密，会话秘钥加密
            Secret secret = clz.getAnnotation(Secret.class);

            if (secret != null) {
                //循环查询到的结果集
                list.forEach(entity -> {
                    //循环每个字段，查找加密字段
                    this.handleEntity(entity);
                });
            }
        }
    }

    /**
     * 处理返回实体类
     *
     * @param entity
     */
    private void handleEntity(Object entity) {

        Class<?> clz = entity.getClass();

        ReflectionUtils.doWithFields(clz, field -> {
            SecretField secretField = field.getAnnotation(SecretField.class);
            if (secretField != null) {
                // 这个字段需要重新赋值
                ReflectionUtils.makeAccessible(field);
                // 原来的值
                Object originValue = field.get(entity);
                // 将数据库中的密文解析成明文
                String plainValue = DbSecretUtil.decrypt((String) originValue, TokenConstant.SECRET);
                // 将明文使用会话秘钥加密
                field.set(entity, plainValue);
            }

        });
    }
}
